import { Engine } from '@babylonjs/core/Engines/engine';
import { Camera } from '@babylonjs/core/Cameras/camera';
import { ScreenshotTools } from "@babylonjs/core/Misc/screenshotTools";
import { EngineOptions } from '@babylonjs/core/Engines/thinEngine';


/**
 * 图像信息对象
 * @param name 名称
 * @param width 宽（像素）
 * @param height 高（像素）
 */
interface IImageInformation {
    name?: string,
    width?: number,
    height?: number,
}

/**
 * 截屏参数对象
 * @param imagesInformation 图像信息对象集合
 * @param precision 精度
 * @param mimeType 格式
 * @param samples 采样数
 * @param antialias 是否抗锯齿
 * @param useRenderTarget 是否使用渲染对象
 * @param download 是否下载
 */
interface IScreenShotOptions {
    imagesInformation?: Array<IImageInformation>,
    precision?: number,
    mimeType?: string,
    samples?: number,
    antialias?: boolean,
    useRenderTarget?: boolean,
    download?: boolean,
}

/**
 * 默认参数
 */
const DEFAULT_OPTIONS: IScreenShotOptions = {
    useRenderTarget: false,
    antialias: true,
    samples: 1,
    mimeType: 'image/png',
    precision: 1,
    download: true,
    imagesInformation: [
        {
            name: 'default',
            width: 1024,
            height: 512,
        }
    ]
};

/**
 * 截取渲染图像
 */
class ScreenShot {

    /**
     * 合并参数
     * @param options 参数对象
     * @returns 合并后的参数对象
     */
    private static mergeOptions(options?: IScreenShotOptions): IScreenShotOptions {
        if (!options) return DEFAULT_OPTIONS;
        const ops: IScreenShotOptions = {};
        for (const key in DEFAULT_OPTIONS) {
            (ops as any)[key] = (options as any)[key] !== undefined ?
                (options as any)[key] :
                (DEFAULT_OPTIONS as any)[key];
        }
        return ops;
    }

    /**
     * 检测引擎相关特性是否开启
     * @param engine 引擎
     * @returns 是否满足截取要求
     */
    private static checkEngine(engine: Engine): boolean {
        const pdb = !!(engine.getCreationOptions() as EngineOptions).preserveDrawingBuffer;
        if (!pdb) {
            console.warn('To using screenshot,engine must be created with [preserveDrawingBuffer]!');
        }
        return pdb;
    }

    /**
     * 截取图像
     * @param engine 引擎
     * @param camera 相机
     * @param options 参数对象
     * @returns 图像数据promise对象
     */
    public static async save(engine: Engine, camera: Camera, options?: IScreenShotOptions): Promise<Array<string>> {
        if (!this.checkEngine(engine)) return [];

        const datas: string[] = [];
        const ops = this.mergeOptions(options);
        const fix = '.' + ops.mimeType?.split('/')[1];
        const width = engine.getRenderWidth();
        const height = engine.getRenderHeight();

        for (let i = 0; i < ops.imagesInformation!.length; i++) {
            const imageInformation = ops.imagesInformation![i];
            const w = imageInformation.width || width;
            const h = imageInformation.height || height;

            try {
                if (ops.useRenderTarget) {
                    await ScreenshotTools.CreateScreenshotUsingRenderTargetAsync(
                        engine, camera,
                        {
                            width: w,
                            height: h,
                            precision: ops.precision
                        },
                        ops.mimeType,
                        ops.samples,
                        ops.antialias,
                        ops.download ? (imageInformation.name + fix) || ('default' + fix) : undefined
                    ).then((data: string) => {
                        datas.push(data);
                        this.downloadImage(data, ops, imageInformation, fix);
                    }, (err) => {
                        console.error('error happened when capturing screen using render target:', err);
                    });
                } else {
                    const ref = height / width;
                    const r = h / w;
                    const precision = r > ref ? w / width : h / height;
                    await ScreenshotTools.CreateScreenshotAsync(
                        engine, camera,
                        {
                            width: width,
                            height: height,
                            precision: precision
                        },
                        ops.mimeType
                    ).then((data: string) => {
                        datas.push(data);
                        this.downloadImage(data, ops, imageInformation, fix);
                    }, (err) => {
                        console.error('error happened when capturing screen:', err);
                    });
                }
            } catch (err) {
                console.error('unknown error happened when capturing screen:', err);
            }

        }

        return datas;
    }

    /**
     * 下载图像
     * @param data 图像数据
     * @param ops 参数对象
     * @param imageInformation 图像信息
     * @param fix 格式后缀
     */
    private static downloadImage(data: string, ops: IScreenShotOptions, imageInformation: IImageInformation, fix: string): void {
        if (!ops.download) return;
        const width = imageInformation.width;
        const height = imageInformation.height;
        const img = new Image(width, height);
        img.src = data;
        const dataUrl = data;
        const aLink = document.createElement('a');
        aLink.download = (imageInformation.name || 'default') + fix;
        aLink.href = dataUrl;
        aLink.click();
    }

}


export { ScreenShot };
export type { IScreenShotOptions, IImageInformation };